
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class FileSplitterAdvanced {
    // 4GB的字节数 (4 * 1024^3)
    private static final long MAX_CHUNK_SIZE = 4L * 1024 * 1024 * 1024;
    // 切分文件的特殊后缀
    private static final String CHUNK_SUFFIX = ".chunk";
    // 专属文件夹后缀
    private static final String SPLIT_FOLDER_SUFFIX = "_split";
    private static final Scanner scanner = new Scanner(System.in);

    public static void main(String[] args) {
        System.out.println("===== 高级文件切分合并工具 =====");
        System.out.println("特性：");
        System.out.println("1. 切分后自动删除原文件");
        System.out.println("2. 合并后自动删除切分文件及专属文件夹");
        System.out.println("3. 切分文件存放于【原文件名" + SPLIT_FOLDER_SUFFIX + "】文件夹");
        System.out.println("4. 切分文件带" + CHUNK_SUFFIX + "后缀");
        System.out.println("---------------------");
        System.out.println("1. 切分单个文件");
        System.out.println("2. 合并单个文件（从专属文件夹）");
        System.out.println("3. 批量切分文件夹中所有文件");
        System.out.println("4. 批量合并所有专属文件夹中的文件");
        System.out.print("请选择操作（1-4）：");

        // 选择操作类型
        int choice;
        try {
            choice = Integer.parseInt(scanner.nextLine().trim());
            if (choice < 1 || choice > 4) {
                System.out.println("无效选择！请输入1-4");
                return;
            }
        } catch (NumberFormatException e) {
            System.out.println("输入错误！请输入数字1-4");
            return;
        }

        try {
            switch (choice) {
                case 1:
                    System.out.print("请输入需要切分的文件路径：");
                    String singleFilePath = scanner.nextLine().trim();
                    splitSingleFile(new File(singleFilePath), true);
                    System.out.println("✅ 切分完成！原文件已删除，切分文件存放于：原文件同级目录/原文件名" + SPLIT_FOLDER_SUFFIX);
                    break;
                case 2:
                    System.out.print("请输入专属文件夹路径（如xxx" + SPLIT_FOLDER_SUFFIX + "）：");
                    String chunkFolderPath = scanner.nextLine().trim();
                    mergeFromFolder(new File(chunkFolderPath), true);
                    System.out.println("✅ 合并完成！切分文件及专属文件夹已删除，原文件已还原");
                    break;
                case 3:
                    System.out.print("请输入包含待切分文件的文件夹路径：");
                    String splitRootPath = scanner.nextLine().trim();
                    batchSplitFolder(new File(splitRootPath), true);
                    System.out.println("✅ 批量切分完成！所有原文件已删除，切分文件存放于各自的" + SPLIT_FOLDER_SUFFIX + "文件夹");
                    break;
                case 4:
                    System.out.print("请输入包含所有" + SPLIT_FOLDER_SUFFIX + "文件夹的根目录：");
                    String mergeRootPath = scanner.nextLine().trim();
                    batchMergeFolders(new File(mergeRootPath), true);
                    System.out.println("✅ 批量合并完成！所有切分文件及文件夹已删除，原文件已还原");
                    break;
            }
        } catch (IOException e) {
            System.out.println("❌ 操作失败：" + e.getMessage());
        } finally {
            scanner.close();
        }
    }

    /**
     * 切分单个文件，支持切分后删除原文件
     */
    private static void splitSingleFile(File sourceFile, boolean deleteSource) throws IOException {
        if (!sourceFile.exists() || !sourceFile.isFile()) {
            throw new FileNotFoundException("源文件不存在或无效：" + sourceFile.getAbsolutePath());
        }

        String sourceDir = sourceFile.getParent() + File.separator;
        String sourceFileName = sourceFile.getName();
        String chunkFolderName = sourceFileName + SPLIT_FOLDER_SUFFIX;
        String chunkFolderPath = sourceDir + chunkFolderName;

        Files.createDirectories(Paths.get(chunkFolderPath));

        int dotIndex = sourceFileName.lastIndexOf(".");
        String baseName = (dotIndex != -1) ? sourceFileName.substring(0, dotIndex) : sourceFileName;
        String extension = (dotIndex != -1) ? sourceFileName.substring(dotIndex) : "";

        try (InputStream in = new BufferedInputStream(new FileInputStream(sourceFile))) {
            byte[] buffer = new byte[8192];
            int bytesRead;
            long currentChunkSize = 0;
            int chunkNumber = 1;

            OutputStream out = getChunkOutputStream(chunkFolderPath, baseName, extension, chunkNumber);

            while ((bytesRead = in.read(buffer)) != -1) {
                if (currentChunkSize + bytesRead > MAX_CHUNK_SIZE) {
                    int remaining = (int) (MAX_CHUNK_SIZE - currentChunkSize);
                    out.write(buffer, 0, remaining);
                    out.close();

                    chunkNumber++;
                    currentChunkSize = bytesRead - remaining;
                    out = getChunkOutputStream(chunkFolderPath, baseName, extension, chunkNumber);
                    out.write(buffer, remaining, bytesRead - remaining);
                } else {
                    out.write(buffer, 0, bytesRead);
                    currentChunkSize += bytesRead;
                }
            }
            out.close();
        }

        if (deleteSource && !sourceFile.delete()) {
            throw new IOException("切分完成，但删除原文件失败：" + sourceFile.getAbsolutePath());
        }
    }

    /**
     * 从专属文件夹合并文件，支持合并后删除切分文件及文件
     */
    private static void mergeFromFolder(File chunkFolder, boolean deleteChunks) throws IOException {
        if (!chunkFolder.exists() || !chunkFolder.isDirectory()) {
            throw new IOException("专属文件夹不存在或无效：" + chunkFolder.getAbsolutePath());
        }
        if (!chunkFolder.getName().endsWith(SPLIT_FOLDER_SUFFIX)) {
            throw new IOException("文件夹名称不符合规则（需以" + SPLIT_FOLDER_SUFFIX + "结尾）：" + chunkFolder.getName());
        }

        String parentDir = chunkFolder.getParent() + File.separator;
        String originalFileName = chunkFolder.getName().replace(SPLIT_FOLDER_SUFFIX, "");
        String originalFilePath = parentDir + originalFileName;

        if (new File(originalFilePath).exists()) {
            throw new IOException("合并失败：原文件已存在（避免覆盖）：" + originalFilePath);
        }

        // 收集切分文件并过滤不符合规则的文件
        File[] chunkFiles = chunkFolder.listFiles((dir, name) -> name.endsWith(CHUNK_SUFFIX));
        if (chunkFiles == null || chunkFiles.length == 0) {
            throw new IOException("专属文件夹中没有可合并的切分文件（需带" + CHUNK_SUFFIX + "后缀）");
        }

        int dotIndex = originalFileName.lastIndexOf(".");
        String baseName = (dotIndex != -1) ? originalFileName.substring(0, dotIndex) : originalFileName;
        String extension = (dotIndex != -1) ? originalFileName.substring(dotIndex) : "";
        Pattern pattern = Pattern.compile("^" + Pattern.quote(baseName) + "_(\\d+)" + Pattern.quote(extension) + "$");

        List<File> validChunks = new ArrayList<>();
        for (File file : chunkFiles) {
            String nameWithoutSuffix = file.getName().substring(0, file.getName().length() - CHUNK_SUFFIX.length());
            Matcher matcher = pattern.matcher(nameWithoutSuffix);
            if (matcher.matches()) {
                validChunks.add(file);
            } else {
                System.out.println("警告：跳过不符合命名规则的文件：" + file.getName());
            }
        }

        if (validChunks.isEmpty()) {
            throw new IOException("没有符合规则的切分文件（命名格式应为：" + baseName + "_序号" + extension + CHUNK_SUFFIX + "）");
        }

        // 按序号排序（修复异常点）
        validChunks.sort((f1, f2) -> {
            String name1 = f1.getName().substring(0, f1.getName().length() - CHUNK_SUFFIX.length());
            String name2 = f2.getName().substring(0, f2.getName().length() - CHUNK_SUFFIX.length());

            Matcher m1 = pattern.matcher(name1);
            Matcher m2 = pattern.matcher(name2);
            m1.matches();  // 已通过校验，确保匹配成功
            m2.matches();

            int num1 = Integer.parseInt(m1.group(1));
            int num2 = Integer.parseInt(m2.group(1));
            return Integer.compare(num1, num2);
        });

        // 合并文件
        try (OutputStream out = new BufferedOutputStream(new FileOutputStream(originalFilePath))) {
            byte[] buffer = new byte[8192];
            for (File chunk : validChunks) {
                try (InputStream in = new BufferedInputStream(new FileInputStream(chunk))) {
                    int bytesRead;
                    while ((bytesRead = in.read(buffer)) != -1) {
                        out.write(buffer, 0, bytesRead);
                    }
                }
                System.out.println("已合并：" + chunk.getName());
            }
        }

        // 清理切分文件和文件夹
        if (deleteChunks) {
            for (File chunk : validChunks) {
                if (!chunk.delete()) {
                    System.out.println("警告：无法删除切分文件：" + chunk.getAbsolutePath());
                }
            }
            if (!chunkFolder.delete()) {
                throw new IOException("合并完成，但删除专属文件夹失败：" + chunkFolder.getAbsolutePath());
            }
        }
    }

    /**
     * 批量切分文件夹中所有文件
     */
    private static void batchSplitFolder(File rootFolder, boolean deleteSource) throws IOException {
        if (!rootFolder.exists() || !rootFolder.isDirectory()) {
            throw new IOException("根文件夹不存在或无效：" + rootFolder.getAbsolutePath());
        }

        File[] files = rootFolder.listFiles(File::isFile);
        if (files == null || files.length == 0) {
            throw new IOException("根文件夹中没有可切分的文件");
        }

        for (File file : files) {
            if (file.getName().endsWith(SPLIT_FOLDER_SUFFIX)) {
                System.out.println("跳过专属文件夹：" + file.getName());
                continue;
            }
            System.out.println("正在切分：" + file.getName());
            splitSingleFile(file, deleteSource);
        }
    }

    /**
     * 批量合并所有专属文件夹中的文件
     */
    private static void batchMergeFolders(File rootFolder, boolean deleteChunks) throws IOException {
        if (!rootFolder.exists() || !rootFolder.isDirectory()) {
            throw new IOException("根文件夹不存在或无效：" + rootFolder.getAbsolutePath());
        }

        File[] chunkFolders = rootFolder.listFiles((dir, name) ->
                new File(dir, name).isDirectory() && name.endsWith(SPLIT_FOLDER_SUFFIX)
        );
        if (chunkFolders == null || chunkFolders.length == 0) {
            throw new IOException("根目录中没有可合并的" + SPLIT_FOLDER_SUFFIX + "文件夹");
        }

        for (File folder : chunkFolders) {
            System.out.println("正在合并专属文件夹：" + folder.getName());
            mergeFromFolder(folder, deleteChunks);
        }
    }

    /**
     * 创建切分文件输出流
     */
    private static OutputStream getChunkOutputStream(String chunkFolderPath, String baseName,
                                                     String extension, int chunkNumber) throws FileNotFoundException {
        String chunkFileName = baseName + "_" + chunkNumber + extension + CHUNK_SUFFIX;
        return new BufferedOutputStream(new FileOutputStream(chunkFolderPath + File.separator + chunkFileName));
    }
}